using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ClientJIP
{
	public class NetworkFile
	{
		// List of available ports
		private Dictionary<Int32,bool> ListPort;
		// Default path for receiving files
		public String DefaultPath { get; private set;}
		
		public NetworkFile (Chat chatClient,String defaultPath)
		{
			//Get default path for saving received files
			DefaultPath = defaultPath;
			//Generate 100 port for receiving files
			ListPort = new Dictionary<int, bool>();
			for(Int32 i=10000;i<11000;i++)
				ListPort.Add(i,false);
		}
		
		/// <summary>
		/// Send to host:port the file defined in the filePath
		/// </summary>
		/// <param name="host">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="port">
		/// A <see cref="Int32"/>
		/// </param>
		/// <param name="filePath">
		/// A <see cref="String"/>
		/// </param>
		public void SendFile(String host, String port, String filePath)
		{
			// Launch thread : check if data received
			Thread thCheckReceivedData = new Thread(SendByte);
        	thCheckReceivedData.Start(new String[]{host,port,filePath});
		}
		
		/// <summary>
		/// Send Byte data to the specified host:port
		/// </summary>
		/// <param name="pseudoAndFile">
		/// A <see cref="System.Object"/>
		/// </param>
		private void SendByte(object paramsArray)
		{
			// Convert object parameters
			String[] paramsArr = (String[])paramsArray;
			String host = paramsArr[0];
			Int32 port = Int32.Parse(paramsArr[1]);
			String filePath = paramsArr[2];
			// Wait for preparing receive
			Thread.Sleep(1000);
			// Tcp Socket
	        TcpClient clientSend = new TcpClient();
			// For reading file
			BinaryReader br = null;
			FileStream fs = null;
			int nbByteLu = 0;
			
			try {
				// Connect to host
				clientSend.Connect(host, port);
		        // Get a stream object for reading and writing
		        NetworkStream stream = clientSend.GetStream();
				// Temporary buffer for sending data
				byte[] tmpBuff = new byte[256];
				
				if(File.Exists(filePath))
				{
			        try
			        {
			            // Open a File
			            fs = File.Open(filePath, FileMode.Open);
			            br = new BinaryReader(fs);
						// Send all file
			            while (fs.Position < fs.Length)
						{
							nbByteLu += br.Read(tmpBuff,0,256);
							stream.Write(tmpBuff,0,256);
						}
			        }
			        finally
			        {
			            if (br != null) br.Close();
			        }
				}
				stream.Close();
				clientSend.Close();
			} catch (Exception ex) {
				Console.WriteLine("Envoi échoué, raison : " + ex);
			}	
			Thread.CurrentThread.Abort();
		}
		
		/// <summary>
		/// Receive file, return the choosen port
		/// </summary>
		/// <param name="size">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="fileName">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="defaultPath">
		/// A <see cref="String"/>
		/// </param>
		/// <returns>
		/// A <see cref="Int32"/>
		/// </returns>
		public Int32 ReceiveFile(String size, String fileName)
		{
			// Search for available port
			Int32[] tmpListPort = (from entry in ListPort where entry.Value == false select entry.Key).ToArray();
			
			if(tmpListPort.Length == 0)
				// No port available
				return -1;
			else {
				Int32 port = tmpListPort[0];
				Console.WriteLine("Préparation envoi");
				// Launch thread : stand for datas
				Thread thCheckReceivedData = new Thread(ReceiveByte);
	        	thCheckReceivedData.Start(new String[]{port.ToString(),DefaultPath,fileName,size});
				// Give to chat choosen port
				return port;
			}
		}
		
		/// <summary>
		/// Receive Byte data from a connected client
		/// </summary>
		/// <param name="SizeAndFilename">
		/// A <see cref="System.Object"/>
		/// </param>
		private void ReceiveByte(object paramsArray)
		{
			// Convert object parameters
			String[] paramsArr = (String[])paramsArray;
			Int32 port = Int32.Parse(paramsArr[0]);
			String defaultPath = paramsArr[1];
			String fileName = paramsArr[2];
			long size = long.Parse(paramsArr[3]);
			// Get own ip interface sent to other user
			//IPAddress localAddr = IPAddress.Parse("127.0.0.1");
			// Create socket for sender
			TcpListener socketReceiveFile = new TcpListener(port);
			// For writing file
			int nbByteLu = 0;
			int nbByteLuTot = 0;
			
			try {
				// Start TcpListener
				socketReceiveFile.Start();
				// Connect TcpClient to TcpListener
				TcpClient clientReceive = socketReceiveFile.AcceptTcpClient();
				// Get a stream object for reading and writing
				NetworkStream netStreamReceive = clientReceive.GetStream();
				// Temporary buffer for receiving data;
				byte[] tmpBuff = new byte[256];
				// Create the new file
				BinaryWriter bw = new BinaryWriter(File.Create(defaultPath + "/" + fileName));
				while(nbByteLuTot < size)
				{
					if((size-nbByteLuTot) > 256) {
						// Receive datas
						nbByteLu = netStreamReceive.Read(tmpBuff,0,256);
						nbByteLuTot += nbByteLu;
						// Write buffer in file
			        	bw.Write(tmpBuff,0,nbByteLu);
					} else {
						// New buffer for end file
						tmpBuff = new byte[(int)(size-nbByteLuTot)];
						// Receive datas
						nbByteLu = netStreamReceive.Read(tmpBuff,0,(int)(size-nbByteLuTot));
						nbByteLuTot += nbByteLu;
						// Write buffer in file
			        	bw.Write(tmpBuff);
						bw.Flush();
					}
				}
				bw.Close();
				clientReceive.Close();
				socketReceiveFile.Stop();
			} catch (Exception ex) {
				Console.WriteLine("Reception échouée, raison : " + ex);
			}
		Thread.CurrentThread.Abort();
	}
	}
}

